package com.objecteffects.ilmp.persist.jpa;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.persistence.EntityManager;

import com.google.inject.Provider;

import com.objecteffects.ilmp.persist.api.IGenericDao;

/**
 * According to a thread on StackOverflow it's safer to inject a
 * Provider<EntityManager> rather than just an EntityManager. With an
 * EntityManager it's possible to have zombie instances of it
 * injected.
 *
 * @author Rusty Wright
 * @param <T>
 */
abstract class AbstractJpaDao<T extends Serializable> implements IGenericDao<T> {
    private final Class<T> clazz;

    private final Provider<EntityManager> entityManagerProvider;

    /*
     * Unfortunately I forgot where I got this code that automatically
     * determines the class and stores it in clazz.
     */
    AbstractJpaDao(final Provider<EntityManager> _entityManagerProvider) {
        final Type t = this.getClass().getGenericSuperclass();
        final ParameterizedType pt = (ParameterizedType) t;

        @SuppressWarnings("unchecked")
        final Class<T> tmp = (Class<T>) pt.getActualTypeArguments()[0];

        this.clazz = tmp;

        this.entityManagerProvider = _entityManagerProvider;
    }

    @Override
    public T findOne(final Long id) {
        return this.getEntityManager().find(this.clazz, id);
    }

    @Override
    public List<T> findAll() {
        @SuppressWarnings("unchecked")
        final List<T> result =
            this.getEntityManager().createQuery("from " + this.clazz.getName())
        .getResultList();

        return result;
    }

    @Override
    public void save(final T entity) {
        this.getEntityManager().persist(entity);
    }

    @Override
    public T update(final T entity) {
        return this.getEntityManager().merge(entity);
    }

    @Override
    public void delete(final T entity) {
        this.getEntityManager().remove(entity);
    }

    @Override
    public void deleteById(final Long entityId) {
        final T entity = this.findOne(entityId);

        this.delete(entity);
    }

    Class<T> getClazz() {
        return this.clazz;
    }

    EntityManager getEntityManager() {
        return this.entityManagerProvider.get();
    }
}
